package com.example.tool.utils;

import com.example.tool.module.ModuleInputEnum;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaDirectoryService;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.codeStyle.CodeStyleManager;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

public class FileUtil {
  public static String upperFirstCase(String str) {
    char[] chars = str.toCharArray();
    if (chars[0] > 91) {
      chars[0] -= 32;
    }
    return String.valueOf(chars);
  }

  public static String lowerFirstCase(String str) {
    char[] chars = str.toCharArray();
    if (chars[0] < 91) {
      chars[0] += 32;
    }
    return String.valueOf(chars);
  }

  public static String readFile(String filePath) {
    return readFile(filePath, true);
  }

  public static String readFile(String filePath, boolean isLineFeed) {
    URL resource = FileUtil.class.getResource(filePath);
    if (resource != null) {
      return readFile(resource, isLineFeed);
    } else {
      return null;
    }
  }

  public static String readFile(URL resource, boolean isLineFeed) {
    try (InputStream inputStream = resource.openStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
      String line;
      StringBuilder stringBuilder = new StringBuilder();
      while ((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line);
        if (isLineFeed) {
          stringBuilder.append("\n");
        }
      }
      return stringBuilder.toString();
    } catch (IOException e) {
      return "";
    }
  }

  public static void copyFile(String oldPath, String outPath) throws IOException {
    copyFile(new FileInputStream(oldPath), outPath);
  }

  public static void copyFile(InputStream inputStream, String outPath) throws IOException {
    BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(outPath));
    int len;
    while ((len = bufferedInputStream.read()) != -1) {
      bufferedOutputStream.write(len);
    }
    byte[] bytes = new byte[1024];
    int size;
    while ((size = bufferedInputStream.read(bytes)) != -1) {
      bufferedOutputStream.write(bytes, 0, size);
    }
    bufferedOutputStream.close();
    bufferedInputStream.close();
  }

  public static void getAllJar(String basePath, Consumer<String> consumer) {
    boolean tagFirst = true;
    boolean tagSecond = true;
    String pathname = basePath + "/ApacheJetspeed/webapps/seeyon/WEB-INF/lib";
    File baseFile = new File(pathname);
    if (baseFile.exists()) {
      getAllFile(pathname, consumer);
    } else {
      tagFirst = false;
    }
    String str = basePath + "/ApacheJetspeed/lib";
    File strFile = new File(str);
    if (strFile.exists()) {
      getAllFileOn(str, consumer, "jsp-api.jar", "servlet-api.jar");
    } else {
      tagSecond = false;
    }
    if (!tagFirst && !tagSecond) {
      // 如果两路径都不存在则获取选中目录中的所有jar包
      // getAllFile(basePath, consumer);
      return;
    }
  }

  public static void getAllFileOn(String basePath, Consumer<String> consumer, String... fileName) {
    Arrays.stream(fileName).forEach(str -> consumer.accept(basePath + "/" + str));
  }

  public static void getAllFile(String basePath, Consumer<String> consumer) {
    File baseFile = new File(basePath);
    File[] files = baseFile.listFiles();
    if (files != null) {
      for (File file : files) {
        // 判断是不是文件
        if (file.isDirectory()) {
          getAllFile(file.getAbsolutePath(), consumer);
        } else {
          if (isJar(file)) {
            consumer.accept(file.getAbsolutePath());
          }
        }
      }
    }
  }

  public static String getEndFile(String filePath) {
    return filePath.substring(filePath.lastIndexOf(".") + 1);
  }

  private static boolean isJar(File file) {
    String name = file.getName();
    if (name.lastIndexOf(".") == -1) {
      return false;
    } else {
      return name.substring(name.lastIndexOf(".") + 1).equals("jar");
    }
  }

  /**
   * 创建文件夹
   * @param virtualFile 目标文件对象
   * @param paths       文件夹名称集合
   */
  public static VirtualFile createDirectories(Object request, VirtualFile virtualFile, String... paths) throws IOException {
    VirtualFile newVirtualFile = virtualFile;
    for (String path : paths) {
      VirtualFile childVirtualFile = newVirtualFile.findChild(path);
      if (childVirtualFile != null && childVirtualFile.exists()) {
        newVirtualFile = childVirtualFile;
      } else {
        newVirtualFile = newVirtualFile.createChildDirectory(request, path);
      }
    }
    return newVirtualFile;
  }


  public static void createClass(Project project, PsiDirectory psiDirectory, Map<ModuleInputEnum, String> replaceParam, String contentPath) {
    createClass(project, psiDirectory, replaceParam, contentPath, true);
  }

  public static void createClass(Project project, PsiDirectory psiDirectory, Map<ModuleInputEnum, String> replaceParam, String contentPath, boolean isOpen) {
    createClass(project, psiDirectory, replaceParam, contentPath, isOpen, null);
  }

  public static void createClass(Project project, PsiDirectory psiDirectory, Map<ModuleInputEnum, String> replaceParam, String contentPath, Function<String, String> otherFun) {
    createClass(project, psiDirectory, replaceParam, contentPath, true, otherFun);
  }

  /**
   * 在指定的路径下创建Java文件，如果该路径下存在此文件则会覆盖原先内容进行重新写入数据
   * @param project      项目对象
   * @param psiDirectory 路径对象
   * @param replaceParam Java文件内容的替换参数
   * @param contentPath  Java文件内容模板路径
   * @param isOpen       表示文件是否在编辑器中打开
   */
  public static void createClass(Project project, PsiDirectory psiDirectory, Map<ModuleInputEnum, String> replaceParam, String contentPath, boolean isOpen, Function<String, String> otherFun) {
    JavaDirectoryService instance = JavaDirectoryService.getInstance();
    PsiPackage psiPackage = instance.getPackage(psiDirectory);
    if (psiPackage != null) {
      String qualifiedName = psiPackage.getQualifiedName();
      String className = replaceParam.get(ModuleInputEnum.CLASS_NANE);
      PsiFile psiFile = psiDirectory.findFile(className + JavaFileType.DOT_DEFAULT_EXTENSION);
      if (psiFile == null) {
        psiFile = psiDirectory.createFile(className + JavaFileType.DOT_DEFAULT_EXTENSION);
      }
      VirtualFile virtualFile = psiFile.getVirtualFile();
      FileDocumentManager documentManager = FileDocumentManager.getInstance();
      Document document = documentManager.getDocument(virtualFile);
      if (document != null) {
        PsiFile finalPsiFile = psiFile;
        WriteCommandAction.runWriteCommandAction(project, () -> {
          String readFile = contentHandle(contentPath, replaceParam, otherFun);
          // 保存类的权限定义名称
          replaceParam.put(ModuleInputEnum.CLASS_ALL_NAME, qualifiedName + "." + className);
          String beanName = replaceParam.get(ModuleInputEnum.BEAN_NANE);
          // 根据是否有Bean的名字来判断是否需要保存类的bean名称
          if (beanName == null) {
            replaceParam.put(ModuleInputEnum.BEAN_NANE, lowerFirstCase(className));
          }
          document.setText("package " + qualifiedName + ";" + readFile);
          documentManager.saveDocument(document);
          // 提交所有文档的更改
          PsiDocumentManager.getInstance(project).commitAllDocuments();
          // 进行代码格式化操作
          CodeStyleManager.getInstance(project).reformat(finalPsiFile);
          if (isOpen) {
            // 用编辑器打开指定文件
            OpenFileDescriptor openFileDescriptor = new OpenFileDescriptor(project, virtualFile);
            FileEditorManager.getInstance(project).openTextEditor(openFileDescriptor, true);
          }
        });
      }
    }
  }

  public static void createXml(Project project, PsiDirectory psiDirectory, String xmlName, Map<ModuleInputEnum, String> replaceParam, String contentPath) {
    createXml(project, psiDirectory, xmlName, replaceParam, contentPath, false);
  }

  public static void createXml(Project project, PsiDirectory psiDirectory, String xmlName, Map<ModuleInputEnum, String> replaceParam, String contentPath, boolean isOpen) {
    createXml(project, psiDirectory, xmlName, replaceParam, contentPath, isOpen, null);
  }

  public static void createXml(Project project, PsiDirectory psiDirectory, String xmlName, Map<ModuleInputEnum, String> replaceParam, String contentPath, Function<String, String> otherFun) {
    createXml(project, psiDirectory, xmlName, replaceParam, contentPath, false, otherFun);
  }

  public static void createXml(Project project, PsiDirectory psiDirectory, String xmlName, Map<ModuleInputEnum, String> replaceParam, String contentPath, Function<String, String> otherFun, boolean cover) {
    createXml(project, psiDirectory, xmlName, replaceParam, contentPath, false, otherFun, cover);
  }

  public static void createXml(Project project, PsiDirectory psiDirectory, String xmlName, Map<ModuleInputEnum, String> replaceParam, String contentPath, boolean isOpen, Function<String, String> otherFun) {
    createXml(project, psiDirectory, xmlName, replaceParam, contentPath, isOpen, otherFun, false);
  }

  /**
   * 在指定的路径下创建xml文件，如果该路径下已经存在该文件。则覆盖源文件写入新数据
   * @param project      项目对象
   * @param psiDirectory 路径对象
   * @param xmlName      xml文件名（不含后缀）
   * @param replaceParam xml文件内容的替换参数
   * @param contentPath  xml文件内容模板路径
   * @param isOpen       表示文件是否在编辑器中打开
   * @param otherFun     文件内容其他操作
   * @param cover        是否覆盖源文件
   */
  public static void createXml(Project project, PsiDirectory psiDirectory, String xmlName, Map<ModuleInputEnum, String> replaceParam, String contentPath, boolean isOpen, Function<String, String> otherFun, boolean cover) {
    WriteCommandAction.runWriteCommandAction(project, () -> {
      boolean isNewFile = false;
      PsiFile psiFile = psiDirectory.findFile(xmlName + XmlFileType.DOT_DEFAULT_EXTENSION);
      if (psiFile == null) {
        isNewFile = true;
        psiFile = psiDirectory.createFile(xmlName + XmlFileType.DOT_DEFAULT_EXTENSION);
      }
      VirtualFile virtualFile = psiFile.getVirtualFile();
      FileDocumentManager documentManager = FileDocumentManager.getInstance();
      Document document = documentManager.getDocument(virtualFile);
      if (document != null) {
        String readFile = contentHandle(contentPath, replaceParam, otherFun);
        if (isNewFile || cover) {
          if (contentPath.contains("spring")) {
            String springXml = FileUtil.readFile("/xml/spring/spring.xml.sample");
            readFile = readFile.substring(0, readFile.length() - 1);
            String replace = springXml.replace("$bean$", readFile);
            document.setText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + replace);
          } else {
            document.setText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + readFile);
          }
        } else {
          String docText = document.getText();
          int tagOffset = docText.lastIndexOf("</beans>");
          if (tagOffset >= 0) {
            document.insertString(tagOffset, readFile);
          }
        }
        documentManager.saveDocument(document);
        // 提交所有文档的更改
        PsiDocumentManager.getInstance(project).commitAllDocuments();
        // 进行代码格式化操作
        CodeStyleManager.getInstance(project).reformat(psiFile);
        if (isOpen) {
          // 用编辑器打开指定文件
          OpenFileDescriptor openFileDescriptor = new OpenFileDescriptor(project, virtualFile);
          FileEditorManager.getInstance(project).openTextEditor(openFileDescriptor, true);
        }
      }
    });
  }

  public static void createOther(Project project, PsiDirectory psiDirectory, String fileName, Map<ModuleInputEnum, String> replaceParam, String contentPath) {
    createOther(project, psiDirectory, fileName, replaceParam, contentPath, false);
  }

  public static void createOther(Project project, PsiDirectory psiDirectory, String fileName, Map<ModuleInputEnum, String> replaceParam, String contentPath, boolean isOpen) {
    String readFile = FileUtil.readFile(contentPath);
    for (ModuleInputEnum moduleInputEnum : replaceParam.keySet()) {
      String key = moduleInputEnum.getKey();
      String value = replaceParam.get(moduleInputEnum);
      readFile = readFile.replaceAll("\\$" + key + "\\$", value);
    }
    createOther(project, psiDirectory, fileName, readFile, isOpen);
  }

  public static void createOther(Project project, PsiDirectory psiDirectory, String fileName, String content) {
    createOther(project, psiDirectory, fileName, content, false);
  }

  /**
   * 在指定的路径下创建文件，如果该路径下已经存在该文件。则覆盖源文件写入新数据
   * @param project      项目对象
   * @param psiDirectory 路径对象
   * @param fileName     文件名称（包含后缀）
   * @param content      文件具体内容
   * @param isOpen       表示文件是否在编辑器中打开
   */
  public static void createOther(Project project, PsiDirectory psiDirectory, String fileName, String content, boolean isOpen) {
    PsiFile psiFile = psiDirectory.findFile(fileName);
    if (psiFile == null) {
      psiFile = psiDirectory.createFile(fileName);
    }
    VirtualFile virtualFile = psiFile.getVirtualFile();
    FileDocumentManager documentManager = FileDocumentManager.getInstance();
    Document document = documentManager.getDocument(virtualFile);
    if (document != null) {
      WriteCommandAction.runWriteCommandAction(project, () -> {
        document.setText(content);
        documentManager.saveDocument(document);
        if (isOpen) {
          // 提交所有文档的更改
          PsiDocumentManager.getInstance(project).commitAllDocuments();
          // 用编辑器打开指定文件
          OpenFileDescriptor openFileDescriptor = new OpenFileDescriptor(project, virtualFile);
          FileEditorManager.getInstance(project).openTextEditor(openFileDescriptor, true);
        }
      });
    }
  }

  /**
   * 字符串模板内容处理
   * @param contentPath  字符串内容模板路径
   * @param replaceParam 模板中替换的字符串参数
   * @param otherFun     其他替换处理方式
   * @return 处理过后的字符串内容
   */
  private static String contentHandle(String contentPath, Map<ModuleInputEnum, String> replaceParam, Function<String, String> otherFun) {
    String readFile = FileUtil.readFile(contentPath);
    for (ModuleInputEnum moduleInputEnum : replaceParam.keySet()) {
      String key = moduleInputEnum.getKey();
      String value = replaceParam.get(moduleInputEnum);
      readFile = readFile.replaceAll("\\$" + key + "\\$", value);
    }
    if (otherFun != null) {
      readFile = otherFun.apply(readFile);
    }
    return readFile;
  }
}
